package com.vids.utils;

import cn.hutool.core.text.CharSequenceUtil;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.github.pagehelper.util.StringUtil;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileFilter;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

@Component
public class QueryUtils {

    public static Map<String, Object> explainParamToMap(String path, String param) throws UnsupportedEncodingException {
	Map<String, Object> map = new HashMap<>();
	if (CharSequenceUtil.isBlank(param)) {
	    return map;
	}
	// 对路径进行解码
	param = URLDecoder.decode(param, "UTF-8");
	// 解决空格/左括号/右括号乱码的问题
	param = param.replace("%20", "").trim();
	// 1：按“&”进行拆分为若干个查询条件
	String[] queryCriteria = param.split("&");
	for (String criteria : queryCriteria) {
	    if (CharSequenceUtil.isNotBlank(criteria)) {
		// 2：根据查询条件内部的逻辑与(AND)和逻辑或(OR)进行拆分，形成若干子条件
		String[] andArr = criteria.split("\\s*AND\\s*");
		for (String ele : andArr) {
		    // 3：若干子条件根据比较运算符进行拆分 例如>=/ <= / etc
		    if (ele.contains(">=")) {
			explainRule(">=", "Big", map, ele);
		    } else if (ele.contains(">")) {
			explainRule(">", "Big", map, ele);
		    } else if (ele.contains("<=")) {
			explainRule("<=", "Small", map, ele);
		    } else if (ele.contains("<")) {
			explainRule("<", "Big", map, ele);
		    } else if (ele.contains("=")) {
			explainRule("=", "", map, ele);
		    }
		}
	    }
	}
	return map;
    }

    private static void explainRule(String spitSymbol, String suffix, Map<String, Object> map, String ele) {
	String[] eleArr = ele.split(spitSymbol);
	if (CharSequenceUtil.isNotBlank(eleArr[0])) {
	    if (eleArr[0].contains(".")) {
		// 4：确定变量名并将对应的值放入map中
		// 对象.属性 的情况
		int index = eleArr[0].lastIndexOf(".");
		String key = removeFirstOrLastBracket(eleArr[0].trim().substring(index + 1));
		map.put((key + suffix).trim(), removeFirstOrLastBracket(eleArr[1].trim()));
	    } else {
		// 自定义变量的情况
		map.put(removeFirstOrLastBracket(eleArr[0].trim()).trim(), removeFirstOrLastBracket(eleArr[1].trim()));
	    }
	}
    }

    // 去掉开头的(和结尾的)
    public static String removeFirstOrLastBracket(String text) {
	if (CharSequenceUtil.isEmpty(text)) {
	    return null;
	}
	int len = text.length();
	int st = 0;
	char[] val = text.toCharArray();
	while ((st < len) && (val[st] == '(')) {
	    st++;
	}
	while ((st < len) && (val[len - 1] <= ')')) {
	    len--;
	}
	return ((st > 0) || (len < text.length())) ? text.substring(st, len) : text;
    }

    // 将自定义关键字的值进行转换方便持久层查询
    public static Map<String, Object> buildParam(Map<String, Object> map) {
	if (map.containsKey("MaxNumRecordReturn")) {
	    if (map.get("MaxNumRecordReturn") != null) {
		map.put("MaxNumRecordReturn", Integer.parseInt(map.get("MaxNumRecordReturn") + ""));
	    }
	}
	if (map.containsKey("PageRecordNum")) {
	    if (map.get("PageRecordNum") != null) {
		map.put("PageRecordNum", Integer.parseInt(map.get("PageRecordNum") + ""));
	    }
	}
	if (map.containsKey("RecordStartNo")) {
	    if (map.get("RecordStartNo") != null) {
		int startNo = Integer.parseInt(map.get("RecordStartNo") + "");
		map.put("RecordStartNo", startNo - 1);
	    }
	}
	if (map.containsKey("Sort")) {
	    if (map.get("Sort") != null) {
		String sortField = map.get("Sort") + "";
		int index = sortField.lastIndexOf(".");
		map.put("Sort", sortField.substring(index + 1));
	    }
	}
	return map;
    }

    public static ObjectMapper getObjectMapperInstance(String filterName, String[] inProArr, String packageName,
	    Class<? extends Annotation> annotationClass) {
	ObjectMapper mapper = new ObjectMapper();
	// 需要跟实体类上的注解@JsonFilter里面的value保持一致
	SimpleFilterProvider filterProvider = new SimpleFilterProvider();
	List<String> annotionList = getAnnotionValue(packageName, annotationClass);
	if (CharSequenceUtil.isNotBlank(filterName)) {
	    annotionList.remove(filterName);
	    if (inProArr != null && inProArr.length > 0) {
		if (filterName.contains("AnalysisRules")) {
		    List<String> list = Arrays.asList(inProArr);
		    if (list.contains("MaxDuration")) {
			list.remove("MaxDuration");
		    }
		    inProArr = (String[]) list.toArray();
		}
		filterProvider.addFilter(filterName, SimpleBeanPropertyFilter.filterOutAllExcept(inProArr));
		for (String value : annotionList) {
		    filterProvider.addFilter(value, SimpleBeanPropertyFilter.filterOutAllExcept(new String[] {}));
		}
	    } else {
		if (filterName.contains("AnalysisRules")) {
		    filterProvider.addFilter(filterName, SimpleBeanPropertyFilter.serializeAllExcept("MaxDuration"));
		} else {
		    filterProvider.addFilter(filterName, SimpleBeanPropertyFilter.serializeAllExcept(new String[] {}));
		}
		for (String value : annotionList) {
		    filterProvider.addFilter(value, SimpleBeanPropertyFilter.serializeAllExcept(new String[] {}));
		}
	    }
	} else {
	    for (String value : annotionList) {
		filterProvider.addFilter(value, SimpleBeanPropertyFilter.serializeAllExcept(new String[] {}));
	    }
	}
	mapper.setFilterProvider(filterProvider);
	return mapper;
    }

    public static List<String> getAnnotionValue(String packageName, Class<? extends Annotation> annotationClass) {
	List<Class<?>> listByAnnotation = getClassListByAnnotation(packageName, JsonFilter.class);
	List<String> list = new ArrayList<String>();
	for (Class<?> clazz : listByAnnotation) {
	    JsonFilter jsonFilter = clazz.getAnnotation(JsonFilter.class);
	    if (jsonFilter != null && CharSequenceUtil.isNotBlank(jsonFilter.value())) {
		list.add(jsonFilter.value());
	    }
	}
	return list;
    }

    public static List<Class<?>> getClassListByAnnotation(String packageName,
	    Class<? extends Annotation> annotationClass) {
	List<Class<?>> classList = new ArrayList<Class<?>>();
	try {
	    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
		    .getResources(packageName.replaceAll("\\.", "/"));
	    while (urls.hasMoreElements()) {
		URL url = urls.nextElement();
		if (url != null) {
		    String protocol = url.getProtocol();
		    if (protocol.equals("file")) {
			String packagePath = url.getPath();
			addClassByAnnotation(classList, packagePath, packageName, annotationClass);
		    } else if (protocol.equals("jar")) {
			JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
			JarFile jarFile = jarURLConnection.getJarFile();
			Enumeration<JarEntry> jarEntries = jarFile.entries();
			while (jarEntries.hasMoreElements()) {
			    JarEntry jarEntry = jarEntries.nextElement();
			    String jarEntryName = jarEntry.getName();
			    if (jarEntryName.endsWith(".class")) {
				String className = jarEntryName.substring(0, jarEntryName.lastIndexOf("."))
					.replaceAll("/", ".");
				Class<?> cls = Class.forName(className);
				if (cls.isAnnotationPresent(annotationClass)) {
				    classList.add(cls);
				}
			    }
			}
		    }
		}
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	}
	return classList;
    }

    private static File[] getClassFiles(String packagePath) {
	return new File(packagePath).listFiles(new FileFilter() {
	    @Override
	    public boolean accept(File file) {
		return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
	    }
	});
    }

    private static String getClassName(String packageName, String fileName) {
	String className = fileName.substring(0, fileName.lastIndexOf("."));
	if (StringUtil.isNotEmpty(packageName)) {
	    className = packageName + "." + className;
	}
	return className;
    }

    private static String getSubPackagePath(String packagePath, String filePath) {
	String subPackagePath = filePath;
	if (StringUtil.isNotEmpty(packagePath)) {
	    subPackagePath = packagePath + "/" + subPackagePath;
	}
	return subPackagePath;
    }

    private static String getSubPackageName(String packageName, String filePath) {
	String subPackageName = filePath;
	if (StringUtil.isNotEmpty(packageName)) {
	    subPackageName = packageName + "." + subPackageName;
	}
	return subPackageName;
    }

    private static void addClassByAnnotation(List<Class<?>> classList, String packagePath, String packageName,
	    Class<? extends Annotation> annotationClass) {
	try {
	    File[] files = getClassFiles(packagePath);
	    if (files != null) {
		for (File file : files) {
		    String fileName = file.getName();
		    if (file.isFile()) {
			String className = getClassName(packageName, fileName);
			Class<?> cls = Class.forName(className);
			if (cls.isAnnotationPresent(annotationClass)) {
			    classList.add(cls);
			}
		    } else {
			String subPackagePath = getSubPackagePath(packagePath, fileName);
			String subPackageName = getSubPackageName(packageName, fileName);
			addClassByAnnotation(classList, subPackagePath, subPackageName, annotationClass);
		    }
		}
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	}
    }

}
